package com.iwuyc.tools.commons.util;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 枚举类工具类
 *
 * @author 吴宇春
 * @version 1.0.0
 * @since 2025-08-10
 */
@Slf4j
public class EnumUtils {
    /**
     * 从给定的枚举中，抽取指定的数据
     *
     * @param enums       指定的枚举
     * @param fieldGetter 字段的获取函数
     * @param <T>         枚举类型
     * @param <R>         抽取的数据类型
     * @return 抽取的数据
     * @author 吴宇春
     * @version 1.0.0
     * @since 2025-05-31
     */
    public static <T extends Enum<T>, R> R extractField(T enums, Function<T, R> fieldGetter) {
        return fieldGetter.apply(enums);
    }

    /**
     * 从给定的枚举中，抽取指定的数据
     *
     * @param enumsClazz 待抽取的枚举类实例
     * @param fieldFunc  字段的获取函数
     * @param <T>        枚举类型
     * @param <R>        抽取的数据类型
     * @return 抽取的数据列表
     * @author 吴宇春
     * @version 1.0.0
     * @since 2025-06-03
     */
    public static <T extends Enum<T>, R> List<R> extractField(Class<T> enumsClazz, Function<T, R> fieldFunc) {
        final T[] enumConstants = enumsClazz.getEnumConstants();
        final List<R> result = new ArrayList<>(enumConstants.length);
        for (T item : enumConstants) {
            result.add(fieldFunc.apply(item));
        }
        return result;
    }

    /**
     * 从给定的枚举中，抽取指定的数据
     *
     * @param enumsClazz 枚举类实例
     * @param fieldFunc  字段的获取函数
     * @param <E>        枚举类型
     * @param <V>        抽取的数据类型
     * @return 抽取的数据列表
     * @author 吴宇春
     * @version 1.0.0
     * @since 2025-06-06
     */
    public static <E extends Enum<E>, V> EnumMap<E, V> extractFieldToMap(Class<E> enumsClazz, Function<E, V> fieldFunc) {
        final EnumMap<E, V> result = new EnumMap<>(enumsClazz);
        for (E item : enumsClazz.getEnumConstants()) {
            result.put(item, fieldFunc.apply(item));
        }
        return result;
    }

    /**
     * 从给定的枚举中，抽取指定的数据，如果key存在冲突，则抛出异常。
     *
     * @param enumsClazz 枚举类实例
     * @param valFunc    字段的获取函数
     * @param <E>        枚举类型
     * @param <V>        抽取的数据类型
     * @return 抽取的数据列表
     * @author 吴宇春
     * @version 1.0.0
     * @since 2025-06-06
     */
    public static <E extends Enum<E>, K, V> Map<K, V> extractFieldToMap(
            Class<E> enumsClazz,
            Function<E, K> keyFunc,
            Function<E, V> valFunc
    ) {
        return extractFieldToMap(enumsClazz, keyFunc, valFunc, null);
    }

    /**
     * 从给定的枚举中，抽取指定的数据，由调用方决定，如果key有冲突的话，是否抛出异常
     *
     * @param enumsClazz               枚举类实例
     * @param valFunc                  字段的获取函数
     * @param keyConflictThrowSupplier 键冲突时抛出异常的提供者，如果为null，则使用默认的异常，如果函数返回的异常为null，则不抛出异常。
     * @param <E>                      枚举类型
     * @param <V>                      抽取的数据类型
     * @return 抽取的数据列表
     * @author 吴宇春
     * @version 1.0.0
     * @since 2025-06-06
     */

    public static <E extends Enum<E>, K, V> Map<K, V> extractFieldToMap(
            Class<E> enumsClazz,
            Function<E, K> keyFunc,
            Function<E, V> valFunc,
            Function<K, RuntimeException> keyConflictThrowSupplier
    ) {
        final E[] enumConstants = enumsClazz.getEnumConstants();
        final Map<K, V> result = new HashMap<>(enumConstants.length);
        Function<K, RuntimeException> keyConflictThrowSupplierInner =
                keyConflictThrowSupplier != null ?
                        keyConflictThrowSupplier :
                        key -> new IllegalArgumentException("key conflict: " + key);
        for (E item : enumConstants) {
            final K key = keyFunc.apply(item);
            if (result.containsKey(key)) {
                final RuntimeException runtimeException = keyConflictThrowSupplierInner.apply(key);
                if (null != runtimeException) {
                    throw runtimeException;
                }
            }

            final V newVal = valFunc.apply(item);
            final V oldVal = result.put(key, newVal);

            // 如果存在旧值，则打印旧值和新值
            if (null != oldVal) {
                log.debug("extractFieldToMap: {} -> {}:{}", item, oldVal, newVal);
            }
        }
        return result;
    }

    /**
     * 根据字段获取函数，搜索等于期望值的枚举
     *
     * @param enumsClazz  枚举类
     * @param fieldGetter 字段获取函数
     * @param expectedVal 期望值
     * @param <E>         枚举类型
     * @param <F>         字段类型
     * @return 搜索结果
     */
    public static <E extends Enum<E>, F> List<E> searchByField(
            Class<E> enumsClazz,
            Function<E, F> fieldGetter,
            F expectedVal
    ) {
        if (null == expectedVal) {
            throw new IllegalArgumentException("expectedVal cannot be null");
        }
        return filterEnum(enumsClazz, e -> expectedVal.equals(fieldGetter.apply(e)));
    }

    /**
     * 根据限定条件，筛选出第一个符合条件的枚举
     *
     * @param enumsClazz 枚举类
     * @param predicate  限定条件
     * @param <E>        枚举的类型的实际类
     * @return 第一个符合条件的枚举
     * @author 吴宇春
     * @version 1.0.0
     * @since 2025-08-16
     */
    public static <E extends Enum<E>> List<E> filterEnum(Class<E> enumsClazz, Predicate<E> predicate) {
        final E[] enums = enumsClazz.getEnumConstants();
        final List<E> result = new ArrayList<>(enums.length);
        for (E item : enums) {
            if (predicate.test(item)) {
                result.add(item);
            }
        }

        return result;
    }
}
